//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "aura.h"
#include "win32.h"
#include "private.h"

AURA_API AuraMutex_Create(
        /* [in] */ Aura_PWChar name,
        /* [in] */ Aura_Bool bLimitToOpenFlag,
        /* [out] */ Aura_Handle * phMutex)
{
    LPCWSTR lpName = (LPCWSTR)(const wchar_t *)name;
    MutexDescr * pMutex = NULL;
    Aura_ECode ec = AURA_NOERROR;

    assert(NULL != phMutex);

    pMutex = (MutexDescr *)Aura_malloc(sizeof(MutexDescr));
    if (NULL == pMutex) {
        ec = AURA_E_OUT_OF_MEMORY;
        goto E_FAIL_EXIT;
    }
    memset((void *)pMutex, 0, sizeof(MutexDescr));

    if (bLimitToOpenFlag) {
        assert(AURA_NULL != name);
        assert(L'\0' != (wchar_t)name[0]);
        pMutex->m_hMutex = OpenMutexW(MUTEX_ALL_ACCESS, FALSE, lpName);
    }
    else {
        pMutex->m_hMutex = CreateMutexW(NULL, FALSE, lpName);
    }

    if (NULL == pMutex->m_hMutex) {
        ec = GetLastECodeError();
        goto E_FAIL_EXIT;
    }
    *phMutex = (Aura_Handle)pMutex;

    return AURA_NOERROR;

E_FAIL_EXIT:
    if (NULL != pMutex) {
        Aura_free((void *)pMutex);
    }
    return ec;
}

AURA_API AuraMutex_Lock(
        /* [in] */ Aura_Handle hMutex,
        /* [in] */ Aura_Millisecond32 timeOut,
        /* [out] */ Aura_WaitResult * pResult)
{
    MutexDescr * pMutex = (MutexDescr *)hMutex;
    ThreadDescr * pSelf = NULL;
    Aura_ECode ec = AURA_NOERROR;

    assert(NULL != pMutex);
    assert(NULL != pResult);

    pSelf = GetCurrentThreadDescr();
    if (pSelf == pMutex->m_pOwner) {
        pMutex->m_count++;
        *pResult = Aura_WaitResult_OK;
        return AURA_NOERROR;
    }
    DWORD res = WaitForSingleObjectEx(pMutex->m_hMutex, (DWORD)timeOut, TRUE);
    switch (res) {
        case WAIT_ABANDONED:
         //  The specified object is a mutex object that was not released
         //  by the thread that owned the mutex object before the owning
         //  thread terminated. Ownership of the mutex object is granted
         //  to the calling thread, and the mutex is set to nonsignaled.
         //  fall throught.
        case WAIT_OBJECT_0:
            *pResult = Aura_WaitResult_OK;
            pMutex->m_count = 0;
            pMutex->m_pOwner = pSelf;
            break;
        case WAIT_TIMEOUT:
            *pResult = Aura_WaitResult_TimedOut;
            break;
        case WAIT_IO_COMPLETION:
            *pResult = Aura_WaitResult_Interrupted;
        default:
            ec = GetLastECodeError();
            break;
    }
    return ec;
}

AURA_API AuraMutex_Unlock(
        /* [in] */ Aura_Handle hMutex)
{
    MutexDescr * pMutex = (MutexDescr *)hMutex;
    BOOL bSuccess = FALSE;

    assert(NULL != pMutex);

    if (pMutex->m_pOwner != GetCurrentThreadDescr()) {
        return AURA_E_NOT_OWNER;
    }

    if (pMutex->m_count > 0) {
        pMutex->m_count--;
        return AURA_NOERROR;
    }

    pMutex->m_pOwner = NULL;
    bSuccess = ReleaseMutex(pMutex->m_hMutex);
    if (!bSuccess) {
        return GetLastECodeError();
    }
    return AURA_NOERROR;
}

AURA_API AuraMutex_Destroy(
        /* [in] */ Aura_Handle hMutex)
{
    MutexDescr * pMutex = (MutexDescr *)hMutex;
    BOOL bSuccess = FALSE;
    assert(NULL != pMutex);

    bSuccess = CloseHandle(pMutex->m_hMutex);
    Aura_free((void *)pMutex);
    if (!bSuccess) {
        return GetLastECodeError();
    }

    return AURA_NOERROR;
}


AURA_API AuraCriticalSection_Create(
        /* [out] */ Aura_Handle * phCriticalSection)
{
    LPWIN_CRITICAL_SECTION pCriticalSection;

    assert(NULL != phCriticalSection);

    pCriticalSection = (LPWIN_CRITICAL_SECTION)\
                Aura_malloc(sizeof(WIN_CRITICAL_SECTION));
    if (NULL == pCriticalSection) {
        return AURA_E_OUT_OF_MEMORY;
    }

    InitializeCriticalSection(pCriticalSection);
    *phCriticalSection = (Aura_Handle)pCriticalSection;

    return AURA_NOERROR;
}

AURA_API AuraCriticalSection_Enter(
        /* [in]  */ Aura_Handle hCriticalSection)
{
    assert(NULL != hCriticalSection);
    EnterCriticalSection((LPWIN_CRITICAL_SECTION)hCriticalSection);

    return AURA_NOERROR;
}

AURA_API AuraCriticalSection_TryEnter(
        /* [in] */ Aura_Handle hCriticalSection, Aura_Bool *pbEntered)
{
    assert(NULL != hCriticalSection);
    assert(NULL != pbEntered);

    BOOL bEnter = TryEnterCriticalSection(
                    (LPWIN_CRITICAL_SECTION)hCriticalSection);
    if (!bEnter)
        *pbEntered = FALSE;
    else
        *pbEntered = TRUE;

    return AURA_NOERROR;
}


AURA_API AuraCriticalSection_Leave(
        /* [in]  */ Aura_Handle hCriticalSection)
{
    assert(NULL != hCriticalSection);
    LeaveCriticalSection((LPWIN_CRITICAL_SECTION)hCriticalSection);

    return AURA_NOERROR;
}

AURA_API AuraCriticalSection_Destroy(
        /* [in]  */ Aura_Handle hCriticalSection)
{
    assert(NULL != hCriticalSection);
    DeleteCriticalSection((LPWIN_CRITICAL_SECTION)hCriticalSection);
    Aura_free((void *)hCriticalSection);

    return AURA_NOERROR;
}

